package generic;

import java.util.ArrayList;
import java.util.List;

/**
 * Creared with IntelliJ IDEA.
 * Description:通配符 通配符是用来解决泛型无法协变的问题的，List<Studeng> 应该是List<Person>的子类
 * 但是<>内的东西都被擦除掉了就不构成父子类关系了，而通配符就可以解决这个问题<?> 泛型T相当于是一个变量等待传入具体类型，
 * 而通配符相当于一种规定规定能传入哪些类型
 * User:yxd
 * Date:2022-02-20
 * Time:15:53
 */
class Test{
    public static <T> void print(ArrayList<T> list){
        for (T t:list) {//这里相当于我知道了类型就是T类型
            System.out.println(t);
        }
    }
    public static  void print1(ArrayList<?> list){
        for (Object t:list) {//此时就不知道是什么类型了 此时相当于是可以传入任何类型
            System.out.println(t);
        }
    }
    /*
    ArrayList<? extends Number>（通配符的上界） 就是 ArrayList<Integer>和ArrayList<Double>的父类了
    ArrayList<?>是ArrayList<? extends Number>的父类前面相当于继承了Object 这样解决了协变类型
     */
    /*通配符的下界
    可以传入类型实参是 Integer的父类的任意类型的
     public static void printAll(MyArrayList<? super Integer> list) {
    ...
    }
     printAll(new MyArrayList<Integer>());
    printAll(new MyArrayList<Number>());
    printAll(new MyArrayList<Object>());
     以下调用是编译错误的
    printAll(new MyArrayList<String>());
    printAll(new MyArrayList<Double>());*/
}
class Person{

}
class Student extends Person{

}
public class Wild {
    public static void main(String[] args) {
        //上界
        ArrayList<Integer> arrayList1 = new ArrayList<>();
        ArrayList<Double> arrayList2 = new ArrayList<>();
        List<? extends Number> list = arrayList1;
        //list.add(1,1);//报错，此时list的引用的子类对象有很多（Number和Number的子类），再添加的时候，任何子类型都可以，为了安全，java不让这样进行添加操作。
         Number a = list.get(0);//可以通过
        //Integer i = list.get(0);//编译错误，只能确定是Number子类，父类接受就可以了通配符的上界不适合写入数据 适合读取数据


        //下界
        ArrayList<? super Person> list2 = new ArrayList<>();
        list2.add(new Person());//只能添加Person和person的子类
        list2.add(new Student());//此时list2引用的类型肯定是Peroson或Person的父类
        // 因此只要比Person的粒度小都可以放入就是其子类，但是大于Person的话就不能放入了
        //但是下界适合写数据 不能读取元素 ，能拿Object来接受
        Object objects = list2.get(0);
        //Person person = list2.get(0);此时就出错了 此时Person是下界 你读取的时候如果给Person开了口子
        // 那么你就可以向里面添加非Person的对象这显然是不安全的 此时编译器只知道 你获得的元素是基类类型或超类类型但并不知他具体是什么类型
        // 不同于上界的向下转型而进行向上转型你又无法确定是否真的你获得的类型的超类类型除非你一直向上转型转型到Object
    }
}
